//
//  PubSub.h
//  sad
//
//  Created by Christopher Kemsley on 2/1/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#ifndef sad_PubSub_h
#define sad_PubSub_h

#include "IAAny.hpp"
#include <map>
#include <string>
#include <iostream>
#include "Log.hpp"
#include "Exception.hpp"

NamespaceBegin





template <typename Type>
class	PublicationContext ;

template <typename Type>
class	Subscription ;

template <typename Type>
class	Publication ;

class	PublicationManager ;







template <typename Type>
class	PublicationContext
{
protected:
	friend class Publication<Type> ;
	friend class Subscription<Type> ;
	friend class PublicationManager ;
	
	PublicationContext ()
	:	mIsOwned	( false )
	,	mValue		( 0 )
		{
		
		}
	
	bool	mIsOwned ;
	Type*	mValue ;
} ;





class	PublicationManager
{
protected:
	template <typename U>	friend class Publication ;
	template <typename U>	friend class Subscription ;
	
	static std::map<std::string,Any>&	map ()
		{
		static std::map<std::string,Any> m ;
		return m ;
		}
	
	template <typename Type>
	static PublicationContext<Type>*	map ( std::string const & name )
		{
		if ( name.size() == 0 )
			{
			throw Exception ( "PublicationManager" , "Attempted to subscribe or publish with a 0-length string" , __FILE__,__LINE__ ) ;
			}
		Any& v = map() [name] ;
		if ( ! v.hasValue() )
			v = new PublicationContext<Type> () ;
		return v . cast<PublicationContext<Type>*> () ;
		}
} ;

















template <typename Type>
class	Subscription
{
public:
	
	Subscription		()
	:	mContext ( 0 )
		{
		}
	Subscription		( std::string const & name )
	:	mContext ( 0 )
		{
		(*this) = name ;
		}
	Subscription		( Subscription const & copy )
	:	mContext(copy.mContext)
		{
		}
	~Subscription		()
		{
		}
	
	Type*			operator->	() const
		{
		if ( mContext == 0 )
			return 0 ;
		return mContext->mValue ;
		}
	Type&			operator*	() const
		{
		if ( mContext == 0 )
			return *(Type*)0 ;
		return *(mContext->mValue) ;
		}
	bool			operator==	( Type* p ) const
		{
		if ( mContext != 0 )
			return mContext->mValue == p ;
		return p==0 ;
		}
	bool			operator!=	( Type* p ) const
		{
		return ! ( (*this) == p ) ;
		}
	
	virtual void	operator=	( std::string const & name )
		{
		mContext	=	PublicationManager::map<Type>(name) ;
		}
	
	// true if a name has been set
	bool			isConnected	() const
		{
		return mContext != 0 ;
		}
	
protected:
	PublicationContext<Type>*	mContext ;
} ;




template <typename Type>
class	Publication : public Subscription<Type>
{
public:
	
	Publication			()
	:	Subscription<Type> ()
		{
		}
	Publication			( std::string const & name )
	:	Subscription<Type> ()
		{
		(*this) = name ;
		}
	~Publication		()
		{
		if ( this->mContext != 0 ) this->mContext->mIsOwned = false ;
		}
	
	
	virtual void	operator=	( std::string const & name )
		{
		if ( this->mContext != 0 ) this->mContext->mIsOwned = false ;
		this->mContext	=	PublicationManager::map<Type>(name) ;
		if ( this->mContext->mIsOwned )
			{
			ThrowException ( "Publication" , "Error: duplicate publisher for \"" << name << "\"" , __FILE__,__LINE__ ) ;
			}
		else
			{
			Log::notice("Publication") << "Successfully published \"" << name << "\"" ;
			}
		this->mContext->mIsOwned = true ;
		}
	virtual void	operator=	( Type * value )
		{
		if ( this->mContext == 0 )
			{
			ThrowException ( "Publication" , "Error: setting publisher, but not publishing" , __FILE__,__LINE__ ) ;
			}
		this->mContext->mValue = value ;
		}
	
	
	// There is no way to undo this method
	static void		publish		( std::string const & name , Type * value )
		{
		// This leaks. In this environment, we might not really care.
		Publication<Type> & it = * new Publication<Type> (name) ;
		it = value ;
		}
} ;







NamespaceEnd

#endif
